package org.ative.chs;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PipedOutputStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

public class UciReaderThread extends Thread {
	
	private boolean uciOk = false;
	private boolean readyOk = false;
	private boolean isStopped = false;	
	
	private UciId uciId = new UciId();
	private UciInfo uciInfo = new UciInfo();
	private ArrayList<UciOption> uciOptions = new ArrayList<UciOption>();
	private Map<Integer, Writer> oswMap = new HashMap<Integer, Writer>();
	private Map<Integer, InputStream> isMap = new HashMap<Integer, InputStream>();
	private UciBestMove uciBestMove;
	
	private UciPipedInputStream depthInputStream = new UciPipedInputStream();
	private PipedOutputStream depthOutputStream;
	private Writer depthStreamWriter;
	
	private UciPipedInputStream npsInputStream = new UciPipedInputStream();
	private PipedOutputStream npsOutputStream;
	private Writer npsStreamWriter;
	
	private UciPipedInputStream pvInputStream = new UciPipedInputStream();
	private PipedOutputStream pvOutputStream;
	private Writer pvStreamWriter;
	
	private UciPipedInputStream currmoveInputStream = new UciPipedInputStream();
	private PipedOutputStream currmoveOutputStream;
	private Writer currmoveStreamWriter;
	
	private UciPipedInputStream topEvalInputStream = new UciPipedInputStream();
	private PipedOutputStream topEvalOutputStream;
	private Writer topEvalStreamWriter;
	
	private UciPipedInputStream bestMoveInputStream = new UciPipedInputStream();
	private PipedOutputStream bestMoveOutputStream;
	private Writer bestMoveStreamWriter;
	
	private UciPipedInputStream nodesInputStream = new UciPipedInputStream();
	private PipedOutputStream nodesOutputStream;
	private Writer nodesStreamWriter;
	
    private BufferedReader reader = null;
    
    private static Logger logger = Logger.getLogger(UciReaderThread.class.getName());
    
    public UciReaderThread(BufferedReader reader) {
        this.reader = reader;

        try {
            //set up the engine depth streams
        	depthOutputStream = new PipedOutputStream(depthInputStream);
			depthStreamWriter = new BufferedWriter(new OutputStreamWriter(depthOutputStream));
			oswMap.put(UciStreamNames.DEPTH.ordinal(), depthStreamWriter);
			isMap.put(UciStreamNames.DEPTH.ordinal(), depthInputStream);
			//set up the engine nps streams
			npsOutputStream = new PipedOutputStream(npsInputStream);
			npsStreamWriter = new BufferedWriter(new OutputStreamWriter(npsOutputStream));
			oswMap.put(UciStreamNames.NPS.ordinal(), npsStreamWriter);
			isMap.put(UciStreamNames.NPS.ordinal(), npsInputStream);
			//set up the engine pv streams
			pvOutputStream = new PipedOutputStream(pvInputStream);
			pvStreamWriter = new BufferedWriter(new OutputStreamWriter(pvOutputStream));
			oswMap.put(UciStreamNames.PV.ordinal(), pvStreamWriter);
			isMap.put(UciStreamNames.PV.ordinal(), pvInputStream);
			//set up the engine pv streamsw
			currmoveOutputStream = new PipedOutputStream(currmoveInputStream);
			currmoveStreamWriter = new BufferedWriter(new OutputStreamWriter(currmoveOutputStream));
			oswMap.put(UciStreamNames.CURRMOVE.ordinal(), currmoveStreamWriter);
			isMap.put(UciStreamNames.CURRMOVE.ordinal(), currmoveInputStream);
			//set up the engine pv streams
			topEvalOutputStream = new PipedOutputStream(topEvalInputStream);
			topEvalStreamWriter = new BufferedWriter(new OutputStreamWriter(topEvalOutputStream));
			oswMap.put(UciStreamNames.TOPEVAL.ordinal(), topEvalStreamWriter);
			isMap.put(UciStreamNames.TOPEVAL.ordinal(), topEvalInputStream);
			//set up the engine bestmove streams
			bestMoveOutputStream = new PipedOutputStream(bestMoveInputStream);
			bestMoveStreamWriter = new BufferedWriter(new OutputStreamWriter(bestMoveOutputStream));
			oswMap.put(UciStreamNames.BESTMOVE.ordinal(), bestMoveStreamWriter);
			isMap.put(UciStreamNames.BESTMOVE.ordinal(), bestMoveInputStream);
			//set up the engine nodes streams
			nodesOutputStream = new PipedOutputStream(nodesInputStream);
			nodesStreamWriter = new BufferedWriter(new OutputStreamWriter(nodesOutputStream));
			oswMap.put(UciStreamNames.NODES.ordinal(), nodesStreamWriter);
			isMap.put(UciStreamNames.NODES.ordinal(), nodesInputStream);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }

    @Override
    public void run() {
        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
            	if (line.equals("uciok")) {
            		this.uciOk = true;
            	} else if (line.equals("readyok")) {
            		this.readyOk = true;
            	} else if (line.startsWith("id")) {
            		UciUtils.parseIdString(uciId, line);
            	} else if (line.startsWith("option")){
            		UciOption uciOption = UciUtils.parseOptionString(line);
            		uciOptions.add(uciOption);
            	} else if (line.startsWith("info")){
            		UciUtils.handleInfoString(oswMap, uciInfo, line);
            	} else if (line.startsWith("bestmove")) {
            		uciBestMove = UciUtils.parseBestMoveString(line);
            		UciUtils.writeOsw(oswMap, UciStreamNames.BESTMOVE.ordinal(), uciBestMove.getBestMove());
            		this.isStopped = true;
            	}
            	//TODO add copyprotection
            	//TODO add registration
            	Thread.yield();
            	if (Thread.currentThread().isInterrupted()) {
				      throw new InterruptedException("Stopped by main");
				}
            }
        }
        catch(IOException exception) {
        	logger.error(exception.getMessage());
        } catch (InterruptedException e) {
			// TODO Auto-generated catch block
        	logger.error(e.getMessage());
		}
    }
    public boolean isReady() {
    	return this.readyOk;
    }
    public boolean isUci() {
    	return this.uciOk;
    }
    public void setStatus(boolean set) {
    	this.readyOk = set;
    }
    public boolean isStopped() {
    	return this.isStopped;
    }
    public void setStopped(boolean set) {
    	this.isStopped = set;
    }
    public UciBestMove getBestMove() {
    	return this.uciBestMove;
    }
    public UciId getUciId() {
    	return this.uciId;
    }
    public List<UciOption> getUciOptions() {
    	return this.uciOptions;
    }
    public UciInfo getUciInfo() {
    	return this.uciInfo;
    }
    public void clearUciInfo() {
    	this.uciInfo = new UciInfo();
    }
    public Map<Integer, InputStream> getInputStreamMap(){
    	return this.isMap;
    }
}